{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mw2VBrBcgvGa"
   },
   "source": [
    "# Week 1 Assignment: Housing Prices\n",
    "\n",
    "In this exercise you'll build a neural network that predicts the price of a house according to a simple formula.\n",
    "\n",
    "Imagine that house pricing is as easy as:\n",
    "\n",
    "A house has a base cost of 50k, and every additional bedroom adds a cost of 50k. This will make a 1 bedroom house cost 100k, a 2 bedroom house cost 150k etc.\n",
    "\n",
    "How would you create a neural network that learns this relationship so that it would predict a 7 bedroom house as costing close to 400k etc.\n",
    "\n",
    "Hint: Your network might work better if you scale the house price down. You don't have to give the answer 400...it might be better to create something that predicts the number 4, and then your answer is in the 'hundreds of thousands' etc."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### TIPS FOR SUCCESSFUL GRADING OF YOUR ASSIGNMENT:\n",
    "\n",
    "- All cells are frozen except for the ones where you need to submit your solutions or when explicitly mentioned you can interact with it.\n",
    "\n",
    "- You can add new cells to experiment but these will be omitted by the grader, so don't rely on newly created cells to host your solution code, use the provided places for this.\n",
    "\n",
    "- You can add the comment # grade-up-to-here in any graded cell to signal the grader that it must only evaluate up to that point. This is helpful if you want to check if you are on the right track even if you are not done with the whole assignment. Be sure to remember to delete the comment afterwards!\n",
    "\n",
    "- Avoid using global variables unless you absolutely have to. The grader tests your code in an isolated environment without running all cells from the top. As a result, global variables may be unavailable when scoring your submission. Global variables that are meant to be used will be defined in UPPERCASE.\n",
    "\n",
    "- To submit your notebook, save it and then click on the blue submit button at the beginning of the page."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "id": "PUNO2E6SeURH",
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "import unittests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 1: create_training_data\n",
    "\n",
    "Your first task is to create the data that your model will be trained on.\n",
    "\n",
    "To generate the training data (aka the features and the targets) you will use numpy to create a one-dimensional tensor with the number of bedrooms and another one-dimensional tensor with the corresponding price in hundreds of thousands of dollars. In this case the number of bedrooms will be the features, which the network will try to map to the target of the prices. **These tensors (or numpy arrays) should have six elements which will be the values (number of bedrooms and price in hundreds of thousands) for houses with 1 up to 6 bedrooms**. \n",
    "\n",
    "**Hints**:\n",
    "- Even if the number of bedrooms can be thought of as an integer, define these values as floats to account for scenarios such as half a bedroom.\n",
    "- The price should also be a float since currency is tipically defined as such."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: create_training_data\n",
    "\n",
    "def create_training_data():\n",
    "    \"\"\"Creates the data that will be used for training the model.\n",
    "\n",
    "    Returns:\n",
    "        (numpy.ndarray, numpy.ndarray): Arrays that contain info about the number of bedrooms and price in hundreds of thousands for 6 houses.\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ###\n",
    "    \n",
    "    # Define feature and target tensors with the values for houses with 1 up to 6 bedrooms\n",
    "    # Hint: Remember to explictly set the dtype as float when defining the numpy arrays\n",
    "    n_bedrooms = None\n",
    "    price_in_hundreds_of_thousands = None\n",
    "\n",
    "    ### END CODE HERE ###\n",
    "\n",
    "    return n_bedrooms, price_in_hundreds_of_thousands"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "features, targets = create_training_data()\n",
    "\n",
    "print(f\"Features have shape: {features.shape}\")\n",
    "print(f\"Targets have shape: {targets.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output:**\n",
    "\n",
    "```\n",
    "Features have shape: (6,)\n",
    "Targets have shape: (6,)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Test your code!\n",
    "unittests.test_create_training_data(create_training_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 2: define_and_compile_model\n",
    "\n",
    "Your second task is to define the architecture of your model and compile it. \n",
    "\n",
    "For this particular task your model should be made up of a **single dense layer with 1 unit** and when compiling it, use:\n",
    " - `Stochastic Gradient Descent` as the optimizer\n",
    " - `Mean Squared Error` as the loss function\n",
    "\n",
    "**Remember that the training data is one-dimensional, so use this information when defining the shape of the Input**.\n",
    "\n",
    "In case you need some extra help, be sure to check the docs for [tf.keras.Input](https://www.tensorflow.org/api_docs/python/tf/keras/Input) and [tf.keras.layers.Dense](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dense)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: define_and_compile_model\n",
    "\n",
    "def define_and_compile_model():\n",
    "    \"\"\"Returns the compiled (but untrained) model.\n",
    "\n",
    "    Returns:\n",
    "        tf.keras.Model: The model that will be trained to predict house prices.\n",
    "    \"\"\"\n",
    "    \n",
    "    ### START CODE HERE ###\n",
    "\n",
    "    # Define your model\n",
    "    model = tf.keras.Sequential([ \n",
    "\t\t# Define the Input with the appropriate shape\n",
    "\t\tNone,\n",
    "\t\t# Define the Dense layer\n",
    "\t\tNone\n",
    "\t]) \n",
    "    \n",
    "    # Compile your model\n",
    "    model.compile(optimizer=None, loss=None)\n",
    "\n",
    "    ### END CODE HERE ###\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "untrained_model = define_and_compile_model()\n",
    "\n",
    "untrained_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output:**\n",
    "\n",
    "```\n",
    "Model: \"sequential\"\n",
    "\n",
    "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
    "┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃\n",
    "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
    "│ dense (Dense)                   │ (None, 1)              │             2 │\n",
    "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
    "\n",
    "Total params: 2 (8.00 B)\n",
    "\n",
    "Trainable params: 2 (8.00 B)\n",
    "\n",
    "Non-trainable params: 0 (0.00 B)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Test your code!\n",
    "unittests.test_define_and_compile_model(define_and_compile_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise 3: train_model\n",
    "\n",
    "With your model and the training data ready now it is time to train your model. You will feed this training data into the model so it can learn the relationship between the number of bedrooms and the price of the houses. For this you will use Tensorflow model's [fit](https://www.tensorflow.org/api_docs/python/tf/keras/Model#fit) method.\n",
    "\n",
    "After training you will test your network with a 7-bedroom house to see if it is able to accurately predict its price."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# GRADED FUNCTION: train_model\n",
    "\n",
    "def train_model():\n",
    "    \"\"\"Returns the trained model.\n",
    "\n",
    "    Returns:\n",
    "        tf.keras.Model: The trained model that will predict house prices.\n",
    "    \"\"\"\n",
    "\n",
    "    ### START CODE HERE ###\n",
    "\n",
    "    # Define feature and target tensors with the values for houses with 1 up to 6 bedrooms\n",
    "    # Hint: Remember you already coded a function that does this!\n",
    "    n_bedrooms, price_in_hundreds_of_thousands = None\n",
    "    \n",
    "    # Define a compiled (but untrained) model\n",
    "    # Hint: Remember you already coded a function that does this!\n",
    "    model = None\n",
    "    \n",
    "    # Train your model for 500 epochs by feeding the training data\n",
    "    model.fit(None, None, epochs=None)\n",
    "    \n",
    "    ### END CODE HERE ###\n",
    "\n",
    "    return model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that you have a function that returns a compiled and trained model when invoked, use it to get the model to predict the price of houses: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "scrolled": true,
    "tags": [
     "graded"
    ]
   },
   "outputs": [],
   "source": [
    "# Get your trained model\n",
    "trained_model = train_model()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output:**\n",
    "\n",
    "Values of loss function don't need to be exact to these values\n",
    "\n",
    "```\n",
    "Epoch 1/500\n",
    "1/1 [==============================] - 0s 409ms/step - loss: 14.7905\n",
    "Epoch 2/500\n",
    "1/1 [==============================] - 0s 5ms/step - loss: 6.8536\n",
    "Epoch 3/500\n",
    "1/1 [==============================] - 0s 7ms/step - loss: 3.1801\n",
    "...\n",
    "...\n",
    "...\n",
    "1/1 [==============================] - 0s 12ms/step - loss: 1.0113e-07\n",
    "Epoch 500/500\n",
    "1/1 [==============================] - 0s 12ms/step - loss: 1.0038e-07\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that your model has finished training it is time to test it out! You can do so by running the next cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": true,
    "id": "kMlInDdSBqGK",
    "tags": []
   },
   "outputs": [],
   "source": [
    "new_n_bedrooms = np.array([7.0])\n",
    "predicted_price = trained_model.predict(new_n_bedrooms, verbose=False).item()\n",
    "print(f\"Your model predicted a price of {predicted_price:.2f} hundreds of thousands of dollars for a {int(new_n_bedrooms.item())} bedrooms house\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Expected Output:**\n",
    "\n",
    "Values of loss function don't need to be exact to these values\n",
    "\n",
    "```\n",
    "Your model predicted a price of 4.03 hundreds of thousands of dollars for a 7 bedrooms house\n",
    "```\n",
    "\n",
    "**The price doesn't need to exactly match this one but it should be close to 4.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Test your code!\n",
    "unittests.test_trained_model(trained_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If everything went as expected you should see a prediction value very close to 4. **If not, try adjusting your code before submitting the assignment.** Notice that you can play around with the value of `new_n_bedrooms` to get different predictions. In general you should see that the network was able to learn the linear relationship between `n_bedrooms` and `price_in_hundreds_of_thousands`, so if you use a value of 8.0 you should get a prediction close to 4.5 and so on."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Congratulations on finishing this week's assignment!**\n",
    "\n",
    "You have successfully coded a neural network that learned the linear relationship between two variables. Nice job!\n",
    "\n",
    "**Keep it up!**"
   ]
  }
 ],
 "metadata": {
  "grader_version": "1",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
